#include "collisionshapesdocument.h"
#include "../gameobjects/graphicsobject.h"
#include "../gameobjects/animation.h"
#include "../gameobjects/texture.h"
#include "../document.h"
#include "../application.h"
#include "../document.h"
#include "collisionshapes.h"
#include "cseditorundocommands.h"

#include <QMimeData>
#include <QClipboard>

CollisionShapesDocument::CollisionShapesDocument(QObject *parent) :
    QObject(parent)
  , m_nCurrentFrame(0)
  , m_fManipulatorSize(4)
{

}

QPointer<CollisionShape> CollisionShapesDocument::createRectShape(const QRectF& rect)
{
    Q_ASSERT(m_gobject);

    QPointer<Document> doc = Application::app().document();
    Q_ASSERT(doc);

    QPointer<CollisionShape> shape = doc->createRectShape(rect);

    QPointer<QUndoStack> us = currentCSUndoStack();
    Q_ASSERT(us);
    us->push(new CSEditorCreateCollisionShapeCmd(*this, shape, true));

    deselectAllShapes();
    selectShape(shape);

    return shape;
}

QPointer<CollisionShape> CollisionShapesDocument::createCircleShape(QPointF center, float radius)
{
    Q_ASSERT(m_gobject);

    QPointer<Document> doc = Application::app().document();
    Q_ASSERT(doc);

    QPointer<CollisionShape> shape = doc->createCircleShape(center, radius);

    QPointer<QUndoStack> us = currentCSUndoStack();
    Q_ASSERT(us);
    us->push(new CSEditorCreateCollisionShapeCmd(*this, shape, true));

    deselectAllShapes();
    selectShape(shape);

    return shape;
}

QPointer<CollisionShape> CollisionShapesDocument::createPolyShape(const QPolygonF &poly)
{
    Q_ASSERT(m_gobject);

    QPointer<Document> doc = Application::app().document();
    Q_ASSERT(doc);

    QPointer<CollisionShape> shape = doc->createPolyShape(poly);

    QPointer<QUndoStack> us = currentCSUndoStack();
    Q_ASSERT(us);
    us->push(new CSEditorCreateCollisionShapeCmd(*this, shape, true));

    deselectAllShapes();
    selectShape(shape);

    return shape;
}

void CollisionShapesDocument::addShape(QPointer<CollisionShape> shape)
{
    if(m_currentAnimation)
    {
        AnimationFrame& frame = m_currentAnimation->frame(m_nCurrentFrame);
        frame.collisionShapes.append(shape);
        shape->setShapeOwner(qobject_cast<AbstractObject*>(m_currentAnimation));
    }
    else
    {
        m_gobject->collisionShapes().append(shape);
        shape->setShapeOwner(qobject_cast<AbstractObject*>(m_gobject));
    }

    emit shapeAdded(shape);
}

QPointer<CollisionShape> CollisionShapesDocument::shapeUnderMouse(const QPointF &mousePos)
{
    QPointer<CollisionShape> result;

    QList<QPointer<CollisionShape> > cShapes = currentCollisionShapes();

    foreach(QPointer<CollisionShape> shape, cShapes)
    {
        QPainterPath shapePath = shape->shape();
        if(shapePath.contains(mousePos))
        {
            result = shape;
            break;
        }
    }

    return result;
}

bool CollisionShapesDocument::deleteShape(QPointer<CollisionShape> shape)
{
    if(m_currentAnimation)
    {
        AnimationFrame& frame = m_currentAnimation->frame(m_nCurrentFrame);
        frame.collisionShapes.removeOne(shape);
    }
    else
    {
        m_gobject->collisionShapes().removeOne(shape);
    }

    emit shapeRemoved(shape);

    shape->deleteLater();

    return true;
}

bool CollisionShapesDocument::deleteSelection()
{
    QList<QPointer<CollisionShape> > selection = selectedShapes();

    foreach(QPointer<CollisionShape> shape, selection)
    {
        deleteShape(shape);
    }

    return true;
}

void CollisionShapesDocument::copySelectedShapes()
{
    QByteArray data;
    QDataStream stream(&data, QIODevice::WriteOnly);

    int numSel = selectedShapes().size();

    stream << (int)numSel;

    foreach(QPointer<CollisionShape> shape, selectedShapes())
    {
        stream << (int)shape->type();

        if(QPointer<CollisionBox> rectangle = qobject_cast<CollisionBox*>(shape))
        {
            stream << rectangle->boxRect();
        }
        else if(QPointer<CollisionCircle> circle = qobject_cast<CollisionCircle*>(shape))
        {
            stream << circle->center();
            stream << circle->radius();
        }
        else if(QPointer<CollisionPoly> polygon = qobject_cast<CollisionPoly*>(shape))
        {
            stream << polygon->poly();
        }
    }

    QMimeData* pMimeData = new QMimeData;
    pMimeData->setData("editor/shapes", data);
    QApplication::clipboard()->setMimeData(pMimeData);
}

void CollisionShapesDocument::pasteShapes()
{
    const QMimeData* pMimeData = QApplication::clipboard()->mimeData();

    if(pMimeData)
    {
        QByteArray data = pMimeData->data("editor/shapes");
        if(data.size())
        {
            QDataStream stream(data);

            int numShapes = 0;
            stream >> numShapes;

            for(int i=0;i<numShapes;++i)
            {
                int type = 0;
                stream >> type;

                CollisionShape::Type shapeType = CollisionShape::Type(type);

                if(shapeType == CollisionShape::Rectangle)
                {
                    QRectF rect;
                    stream >> rect;
                    createRectShape(rect);
                }
                else if(shapeType == CollisionShape::Circle)
                {
                    QPointF center;
                    float radius;

                    stream >> center;
                    stream >> radius;
                    createCircleShape(center, radius);
                }
                else if(shapeType == CollisionShape::Polygon)
                {
                    QPolygonF poly;
                    stream >> poly;
                    createPolyShape(poly);
                }
            }
        }
    }
}

const QList<QPointer<CollisionShape> > &CollisionShapesDocument::currentCollisionShapes() const
{
    Q_ASSERT(m_gobject);

    if(m_currentAnimation)
    {
        AnimationFrame& frame = m_currentAnimation->frame(m_nCurrentFrame);
        return frame.collisionShapes;
    }
    else
    {
        return m_gobject->collisionShapes();
    }
}

void CollisionShapesDocument::currentGraphicsParameters(Texture** texture, QRect &rRect, bool &rIsAnimation)
{
    if(m_currentAnimation)
    {
        rIsAnimation = true;
        rRect = m_currentAnimation->frame(m_nCurrentFrame).rect;
        *texture = m_currentAnimation->texture();
    }
    else
    {
        rIsAnimation = false;
        *texture = m_gobject->texture();
    }
}

void CollisionShapesDocument::selectShape(QPointer<CollisionShape> shape)
{
    m_selectedShapes.append(shape);
    emit shapeSelectionChanged();
}

void CollisionShapesDocument::deselectShape(QPointer<CollisionShape> shape)
{
    if(m_selectedShapes.contains(shape))
    {
        m_selectedShapes.removeOne(shape);
        emit shapeSelectionChanged();
    }
}

void CollisionShapesDocument::selectAllShapes()
{
    m_selectedShapes.append(currentCollisionShapes());
    emit shapeSelectionChanged();
}

void CollisionShapesDocument::deselectAllShapes()
{
    m_selectedShapes.clear();
    emit shapeSelectionChanged();
}

void CollisionShapesDocument::selectShape(const QPointF &pt)
{
    deselectAllShapes();

    QList<QPointer<CollisionShape> > cShapes = currentCollisionShapes();

    foreach(QPointer<CollisionShape> shape, cShapes)
    {
        QPainterPath shapePath = shape->shape();
        if(shapePath.contains(pt))
        {
            selectShape(shape);
            break;
        }
    }

    emit shapeSelectionChanged();
}

void CollisionShapesDocument::selectShapes(const QRectF &area)
{
    deselectAllShapes();

    QList<QPointer<CollisionShape> > cShapes = currentCollisionShapes();

    foreach(QPointer<CollisionShape> shape, cShapes)
    {
        QPainterPath shapePath = shape->shape();
        if(shapePath.intersects(area))
        {
            selectShape(shape);
        }
    }

    emit shapeSelectionChanged();
}

const QList<QPointer<CollisionShape> > &CollisionShapesDocument::selectedShapes() const
{
    return m_selectedShapes;
}

bool CollisionShapesDocument::isSelected(QPointer<CollisionShape> shape) const
{
    return m_selectedShapes.contains(shape);
}

void CollisionShapesDocument::resetDocument()
{
    m_gobject = qobject_cast<GraphicsObject*>(Application::app().document()->currentObject());
    Q_ASSERT(m_gobject);

    m_currentAnimation = 0;
    m_nCurrentFrame = 0;

    deselectAllShapes();
}

QPointer<QUndoStack> CollisionShapesDocument::currentCSUndoStack()
{
    QPointer<QUndoStack> us;

    if(m_currentAnimation)
    {
         us = m_currentAnimation->frame(m_nCurrentFrame).csUndoStack;
    }
    else
    {
        us = m_gobject->csUndoStack();
    }

    return us;
}

void CollisionShapesDocument::setCurrentAnimation(QPointer<Animation> anim)
{
    deselectAllShapes();
    m_currentAnimation = anim;
    emit editingObjectChanged();
}

void CollisionShapesDocument::setCurrentFrame(int frameIndex)
{
    deselectAllShapes();
    m_nCurrentFrame = frameIndex;
    emit editingObjectChanged();
}
